#include <jni.h>
#include <string>

#include <cstdlib>
#include <android/log.h>

#include <cstdio>
#include "abstypes.h"
#include "BeautyShot_Image_Algorithm.h"
#include "BeautyShot_Video_Algorithm.h"

#define MOK 0
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,"TEST",__VA_ARGS__)

#define level 90

//定义算法对象指针和文件指针（全局静态）
static BeautyShot_Video_Algorithm* m_BeautyShot_Video_Algorithm = MNull;
static FILE *fp = nullptr;

//获取当前时间（UTC）
MLong GetCurrentTime()
{
    struct timeval tv{};
    gettimeofday(&tv, nullptr);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

//将源图像数据转换成算法支持的图像格式（图像空间）
MVoid setOffScreen(ASVLOFFSCREEN *pImg, MUInt8 *pData, MInt32 iWidth, MInt32 iHeight)
{
    pImg->u32PixelArrayFormat = ASVL_PAF_NV21;
    pImg->i32Width = iWidth;
    pImg->i32Height = iHeight;
    pImg->pi32Pitch[0] = iWidth;
    pImg->pi32Pitch[1] = iWidth;
    pImg->ppu8Plane[0] = pData;
    pImg->ppu8Plane[1] = pData + iWidth*iHeight;
}

//单帧处理(图像)，在Camera1上不可用，格式不支持
MVoid processImage(MUInt8 *pData,MInt32 iWidth,MInt32 iHeight) {
    LOGD("T1");
    //声明算法实例指针
    BeautyShot_Image_Algorithm* m_BeautyShot_Image_Algorithm = MNull;

    LOGD("T2");
    //创建算法实例（将算法实例指针指向算法实例）
    if(Create_BeautyShot_Image_Algorithm(BeautyShot_Image_Algorithm::CLASSID,
        &m_BeautyShot_Image_Algorithm)!= MOK ||m_BeautyShot_Image_Algorithm == nullptr)
    {
        LOGD("Create_BeautyShot_Image_Algorithm, fail");
        return;
    }

    LOGD("T3");
    //算法实例初始化
    int ret = m_BeautyShot_Image_Algorithm->Init();
    if (ret != MOK){
        LOGD("init,ret = %d",ret);
        return;
    }

    LOGD("T4");
    //准备图像空间实例
    ASVLOFFSCREEN tScreenSrc = {0}; //实例
    LPASVLOFFSCREEN pScreenDst;     //实例指针

    //获取单帧大小
    MInt32 iFrameLength = iWidth*iHeight*3/2;

    LOGD("T5");
    //图像格式转换
    setOffScreen(&tScreenSrc, pData, iWidth, iHeight);
    pScreenDst = &tScreenSrc;

    LOGD("T6");
    //设置处理水平
    m_BeautyShot_Image_Algorithm->SetFeatureLevel(FEATURE_FACE_SOFTEN_KEY ,level);
    //执行处理
    LOGD("T7");
    ret = m_BeautyShot_Image_Algorithm->Process(&tScreenSrc,pScreenDst,MNull,MNull);
    if (ret != MOK)
    {
        LOGD("process fail");
    }
    else LOGD("process succeed");

    LOGD("T8");

    //保存处理后的图像数据
    char pathname[256] = {};
    sprintf(pathname,"/sdcard/b1_picture_%dx%d.nv21",iWidth,iHeight);
    FILE *fp = fopen(pathname, "wbe");

    if (nullptr != fp)
    {
        fwrite(pData, iFrameLength, 1, fp);
        LOGD("save succeed");
        fclose(fp);
    }
    else LOGD("save fail");

    //内存释放
    m_BeautyShot_Image_Algorithm->UnInit();
    m_BeautyShot_Image_Algorithm->Release();
    m_BeautyShot_Image_Algorithm = nullptr;

}

//用作测试
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_szl7033_beauty1_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

//本地函数，作为Java与c/c++间的桥梁，实现相互之间的参数转换
extern "C"
JNIEXPORT void JNICALL
Java_com_example_szl7033_beauty1_MainActivity_previewProcess(
        JNIEnv* env,
        jobject thiz,
        jbyteArray bytes,
        jint width,
        jint height) {

    MLong t1,t2,t3,t4;

    t3 = GetCurrentTime();

    //获取单帧图像数据
    jbyte* byte = env->GetByteArrayElements(bytes, nullptr);

    //确定算法和文件对象可用
    if(m_BeautyShot_Video_Algorithm == nullptr || fp == nullptr){
        LOGD("参数传递失败");
        return;
    }

    //转换图像格式空间
    ASVLOFFSCREEN tScreenSrc = {0};
    LPASVLOFFSCREEN pScreenDst;
    setOffScreen(&tScreenSrc, (MUInt8*)byte, width, height);
    pScreenDst = &tScreenSrc;

    t1 = GetCurrentTime();
    //单帧处理
    int ret = m_BeautyShot_Video_Algorithm->Process(&tScreenSrc,pScreenDst,MNull,MNull);
    t2 = GetCurrentTime();

    if (ret != MOK)
    {
        LOGD("process fail");
    }
    else LOGD("process succeed");

    //保存数据
    if (nullptr != fp)
    {
        fwrite((MUInt8*)byte, static_cast<size_t>(width * height * 3 / 2), 1, fp);
        LOGD("save succeed");
    }
    else LOGD("save fail");

    t4 = GetCurrentTime();

    LOGD("图像空间转换时间：%ld ms，处理时间：%ld ms，图像文件保存时间：%ld ms",t1-t3,t2-t1,t4-t2);
    LOGD("总时间：%ld ms",t4-t3);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_szl7033_beauty1_MainActivity_previewInit(
        JNIEnv* env,
        jobject thiz,
        jint width,
        jint height) {
    MLong t1,t2;
    t1 = GetCurrentTime();
    //获取算法实例
    if(Create_BeautyShot_Video_Algorithm(BeautyShot_Video_Algorithm::CLASSID,
                                         &m_BeautyShot_Video_Algorithm)!= MOK || m_BeautyShot_Video_Algorithm == NULL){
        LOGD("Create_BeautyShot_Video_Algorithm, fail");
        return;
    }

    //算法初始化
    MRESULT ret = m_BeautyShot_Video_Algorithm->Init();
    if (ret != MOK)
        return;

    //设置处理程度
    m_BeautyShot_Video_Algorithm->SetFeatureLevel(FEATURE_FACE_SOFTEN_KEY,level);
    m_BeautyShot_Video_Algorithm->SetFeatureLevel(FEATURE_EYE_ENLARGEMENT_KEY  ,level);
    m_BeautyShot_Video_Algorithm->SetFeatureLevel(FEATURE_FACE_SLENDER_KEY  ,level);
    m_BeautyShot_Video_Algorithm->SetFeatureLevel(FEATURE_SKIN_TONE_BRIGHT_KEY  ,level);

    //打开保存图像数据的文件
    char pathname[64] = {};
    sprintf(pathname,"/sdcard/b1_preview_%dx%d.nv21",width,height);
    fp = fopen(pathname, "wbe");

    t2 = GetCurrentTime();
    LOGD("初始化时间：%ld ms",t2-t1);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_szl7033_beauty1_MainActivity_previewUninit(
        JNIEnv* env,
        jobject /* this */) {
    MLong t1,t2;
    t1 = GetCurrentTime();
    //释放资源
    fclose(fp);
    m_BeautyShot_Video_Algorithm->UnInit();
    m_BeautyShot_Video_Algorithm->Release();
    m_BeautyShot_Video_Algorithm = nullptr;

    t2 = GetCurrentTime();
    LOGD("资源释放时间：%ld ms",t2-t1);
}